using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using DeSleeper.Library;
using DeSleeper.Library.PowerManagement;

namespace DeSleeper.XPowerCfg
{
    class Program
    {
        private const string NEWSCHEMENAME = "Optimized Power Scheme";

        static void Main(string[] args)
        {
            var parsedArguments = new ArgumentParser(args);

            if (parsedArguments.ShouldCreate.GetValueOrDefault(true))
                applySettings(parsedArguments);

            if (parsedArguments.EnableWakeOnLan)
                enableWakeOnLan();

            if (parsedArguments.ShouldList)
            {
                Console.WriteLine();
                foreach (var scheme in  PowerScheme.GetPowerSchemes())
                    writeSchemeHeadline(scheme);
            }

            if (parsedArguments.ShouldQuery)
                performQuery(parsedArguments.QueryScheme);
            if (parsedArguments.ShouldQueryWakeOnLan)
                performEnableWakeOnLanQuery();
        }

        private static void performEnableWakeOnLanQuery()
        {
            foreach (var wakeOnLanNetworkInterface in WakeOnLanNetworkInterface.GetAllRelevantInterfaces())
            {
                Console.WriteLine();
                //                 0    |    1    |    2    |    3    |    4    |    5    |    6    |    7    |    8
                Console.WriteLine("{0}:", wakeOnLanNetworkInterface.BaseInterface.Description);
                Console.WriteLine();
                Console.WriteLine("   Wake-On-Lan enabled         : {0}", wakeOnLanNetworkInterface.WakeOnLanConfiguration.WakeOnLanEnabled);
                Console.WriteLine("   Wake only for magic packets : {0}", wakeOnLanNetworkInterface.WakeOnLanConfiguration.MagicPacketEnabled);
            }
        }

        private static void performQuery(string schemeIdentifier)
        {
            int schemeIndex;
            Guid schemeGuid;
            PowerScheme schemeToQuery;
            if (string.IsNullOrEmpty(schemeIdentifier))
                schemeToQuery = PowerScheme.GetCurrent();
            else if (tryParseGuid(schemeIdentifier, out schemeGuid))
                schemeToQuery = PowerScheme.GetPowerSchemes().SingleOrDefault(scheme => scheme.Guid == schemeGuid);
            else if (Int32.TryParse(schemeIdentifier, out schemeIndex))
                schemeToQuery = PowerScheme.GetPowerSchemes().SingleOrDefault(scheme => scheme.Index == schemeIndex);
            else
                schemeToQuery = PowerScheme.GetPowerSchemes().SingleOrDefault(scheme => 
                                                                              string.Equals(scheme.Name, schemeIdentifier, StringComparison.CurrentCultureIgnoreCase));

            if (schemeToQuery == null)
            {
                Console.WriteLine("Scheme '{0}' does not exist.", schemeIdentifier);
                return;
            }

            Console.WriteLine();
            writeSchemeHeadline(schemeToQuery);
            //                 0    |    1    |    2    |    3    |    4    |    5    |    6    |    7    |    8
            Console.WriteLine("                                      AC              DC");
            Console.WriteLine("Processor Power Setting State: {0, 15} {1,15}",
                              schemeToQuery.ACSettings.ProcessorPowerSettings,
                              schemeToQuery.DCSettings.ProcessorPowerSettings);

            Console.WriteLine("Monitor Timeout              : {0, 15} {1,15}",
                              schemeToQuery.ACSettings.VideoTimeout,
                              schemeToQuery.DCSettings.VideoTimeout);

            Console.WriteLine("Hard Disk Timeout            : {0, 15} {1,15}",
                              schemeToQuery.ACSettings.HardDriveTimeout,
                              schemeToQuery.DCSettings.HardDriveTimeout);

            Console.WriteLine("Standby Timeout              : {0, 15} {1,15}",
                              schemeToQuery.ACSettings.IdleTimeout,
                              schemeToQuery.DCSettings.IdleTimeout);
        }

        private static void writeSchemeHeadline(PowerScheme scheme)
        {
            Console.WriteLine("[{3}]{4}Scheme GUID: {0}, ({1})\n{2}\n", scheme.Guid, scheme.Name, scheme.Description.TrimEnd(), scheme.Index, 
                scheme.Equals(PowerScheme.GetCurrent()) ? "*" : " ");
        }

        private static bool tryParseGuid(string input, out Guid guid)
        {
            try
            {
                guid = new Guid(input);
                return true;
            }
            catch (FormatException)
            {
                guid = Guid.Empty;
                return false;
            }
            catch(OverflowException)
            {
                guid = Guid.Empty;
                return false;
            }
        }

        private static void applySettings(ArgumentParser parsedArguments)
        {
            var currentScheme = PowerScheme.GetCurrent();
            if (!PowerScheme.GetPowerSchemes().Any(
                    scheme => string.Equals(scheme.Name, NEWSCHEMENAME, StringComparison.InvariantCultureIgnoreCase)))
                currentScheme = currentScheme.Duplicate(NEWSCHEMENAME, 
                    string.Format(CultureInfo.CurrentUICulture, "A optimized version of '{0}' created by XPowerCfg.", currentScheme.Name));

            applySettings(currentScheme.ACSettings, parsedArguments.MaximumIdleTimeout.Ac, parsedArguments.MaximumVideoTimeout.Ac, parsedArguments.MaximumHardDriveTimeout.Ac);
            applySettings(currentScheme.DCSettings, parsedArguments.MaximumIdleTimeout.Dc, parsedArguments.MaximumVideoTimeout.Dc, parsedArguments.MaximumHardDriveTimeout.Dc);

            currentScheme.Update();
            PowerScheme.SetCurrent(currentScheme);
        }

        private static void enableWakeOnLan()
        {
            foreach (var wakeOnLanNetworkInterface in WakeOnLanNetworkInterface.GetAllRelevantInterfaces()
                .Where(intf => intf.WakeOnLanConfiguration.WakeOnLanEnabled == ConfigurationState.Disabled ||
                               intf.WakeOnLanConfiguration.MagicPacketEnabled == ConfigurationState.Disabled))
            {
                wakeOnLanNetworkInterface.EnableWakeOnLan();
            }
        }

        private static void applySettings(PowerSettings settings, TimeSpan maximumIdleTimeout, TimeSpan maximumVideoTimeout, TimeSpan maximumHardDriveTimeout)
        {
            if (settings.ProcessorPowerSettings == ProcessorPowerSettings.None)
                settings.ProcessorPowerSettings = ProcessorPowerSettings.Adaptive;

            if (maximumIdleTimeout != TimeSpan.Zero && (settings.IdleTimeout == TimeSpan.Zero || settings.IdleTimeout > maximumIdleTimeout))
                settings.IdleTimeout = maximumIdleTimeout;

            if (maximumVideoTimeout != TimeSpan.Zero && (settings.VideoTimeout == TimeSpan.Zero || settings.VideoTimeout > maximumVideoTimeout))
                settings.VideoTimeout = maximumVideoTimeout;

            if (maximumHardDriveTimeout != TimeSpan.Zero && (settings.HardDriveTimeout == TimeSpan.Zero || settings.HardDriveTimeout > maximumHardDriveTimeout))
                settings.HardDriveTimeout = maximumHardDriveTimeout;
        }
    }
}
